package club.kingyin.easycache.utils;

import club.kingyin.easycache.component.CachePostProcess;
import club.kingyin.easycache.component.annotation.handler.*;
import club.kingyin.easycache.key.AbstractEasyCacheKey;
import club.kingyin.easycache.key.DefaultEasyCacheKey;
import club.kingyin.easycache.component.InjectPostProcess;
import club.kingyin.easycache.component.annotation.Param;
import club.kingyin.easycache.exception.ParameterException;
import club.kingyin.easycache.key.EasyCacheKey;
import club.kingyin.easycache.key.InvokePostProcess;
import club.kingyin.easycache.method.CacheMethod;
import club.kingyin.easycache.method.core.CacheEnhancer;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;

import java.util.Arrays;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class AnnotationUtils {

    public static final String PRAM_NULL = "$_&";

    public static final AnnotationIgnoreFieldPostProcess ignoreFiled = new AnnotationIgnoreFieldPostProcess();
    public static final AnnotationParamFiledPostProcess paramField = new AnnotationParamFiledPostProcess();

    public static final InjectPostProcess[] injects = {ignoreFiled, paramField};

    public static void registerAnnotationHandler(AbstractEasyCacheKey key) {
        key.setPostProcesses(injects);
    }

    public static EasyCacheKey createByParams(Param[] params, DefaultEasyCacheKey.Builder builder, EasyCacheKey key, Set<String> pas) {
        Set<String> ps = new HashSet<>();
        // 解析Param value优先级大于ref，有name属性则以name为参数名，否则以ref的默认参数名
        for (Param param : params) {
            // name 默认
            if (AnnotationUtils.PRAM_NULL.equals(param.name())) {
                // ref默认
                if (AnnotationUtils.PRAM_NULL.equals(param.ref())) {
                    throw new ParameterException("无效的参数 " + key.toString() + "映射参数未定义，请补充name或ref参数");
                } else {
                    // 存在ref name默认 （只需要解析ref）
                    addIfHasParamToKeyThenThrowException(param.ref(), null, builder, key);
                    ps.add(param.ref().replace("${","").replace("}",""));
                }
            } else {
                // name 存在， value默认
                if (AnnotationUtils.PRAM_NULL.equals(param.value())) {
                    // name 存在，value 默认 ref默认
                    if (AnnotationUtils.PRAM_NULL.equals(param.ref())) {
                        throw new ParameterException("无效的参数 " + key.toString() + "无法确定" + param.name() + "映射的值，请补充ref或value参数");
                    } else {
                        // name存在，ref存在，value默认 （需要解析ref和name）
                        addIfHasParamToKeyThenThrowException(param.ref(), param.name(), builder, key);
                    }
                } else {
                    // name存在，value存在 （需要解析name和value） 优先解析value
                    builder.addPram(param.name(), InstanceUtils.switchByType(param.value(), param.type()));
                }
            }
        }
        // 剔除默认的param
        pas = pas == null? ((JSONObject) key.getPrams()).keySet() : pas;
        pas.stream().filter(p->!ps.contains(p)).forEach(builder::ignorePram);
        return builder.build();
    }

    private static void addIfHasParamToKeyThenThrowException(String ref, String name, DefaultEasyCacheKey.Builder builder, EasyCacheKey key) {
        boolean att = false;
        String ctx = null;
        // 解析上下文
        if (ref.startsWith("${")) {
            String r = "\\$\\{([^\\}]+)\\}";
            Pattern pattern = Pattern.compile(r);
            Matcher matcher = pattern.matcher(ref);
            while (matcher.find()) {
                ctx = matcher.group(1);
                if (((AbstractEasyCacheKey) key).ancillary.containsKey(ctx)) {
                    att = true;
                } else {
                    throw new ParameterException("上下文中为找不到 " + ref);
                }
                break;
            }
        }

        if (key.containsPramName(ref) || att && !ref.contains(".")) {
            if (name != null) {
                builder.addPram(name, getByRef(att?ctx:ref, ((AbstractEasyCacheKey) key), att));
            } else {
                builder.addPram(att?ctx:ref, getByRef(att?ctx:ref, ((AbstractEasyCacheKey) key), att));
            }
        } else if (ref.contains(".") || att) {
            // 深层解析 包含.
            String[] field = ref.split("\\.");
            JSONObject prams;
            try {
                prams = (JSONObject) getByRef(att?ctx:ref, ((AbstractEasyCacheKey) key), att);
                if (att) {
                    // 装载ctx
                    builder.addPram(ctx, prams);
                }
            } catch (Exception e) {
                throw new ParameterException("参数 "+ref+" 不支持.操作");
            }
            // 是基本类型，不支持深入解析
            if (AbstractEasyCacheKey.paramIsBasicReferenceType(prams.get(field[0]))) {
                throw new ParameterException("参数不支持解析或者该参数已被修改？ " + ref);
            } else {
                // 非基本对象引用类型，可以深入解析
                JSONObject pre = att ? prams : prams.getJSONObject(field[0]);
                for (int i = 1; i < field.length - 1; i++) {
                    pre = pre.getJSONObject(field[i]);
                }
                if (name != null) {
                    builder.addPram(name, pre.get(field[field.length - 1]));
                } else {
                    builder.addPram(ref.replace("${","").replace("}","")
                            , pre.get(field[field.length - 1]));
                }
            }
        } else {
            throw new ParameterException("没有找到 " + ref);
        }

    }

    private static Object getByRef(String ref, AbstractEasyCacheKey key, boolean att) {
        if (att) {
            return JSON.toJSON(key.obtain(ref));
        } else {
            return ref.contains(".")? key.getPrams() : key.getPrams().get(ref);
        }

    }

    private static CachePostProcess[] saveCache = {
            // CacheHandler 注解处理器
            new AnnotationCacheCustomizationPostProcess(),
            // Expire 注解处理器
            new AnnotationExpireAutoSetPostProcess(),
            // InvokeException 注解处理器
            new AnnotationInvokeExceptionHandler()
    };

    public static Set<CachePostProcess> cachePostProcess() {
        return new LinkedHashSet<>(Arrays.asList(saveCache));
    }

    private static InvokePostProcess<CacheMethod>[] postProcesses = new InvokePostProcess[]{
            // InvokeHandler 注解处理器
            new AnnotationCacheMethodInvokeCustomizationPostProcess(),
            // Module,Pram 注解处理器
            new AnnotationModuleAutoSetPostProcess(),
            // Remove 注解处理器
            new AnnotationRemoveHandler(),
            // 二级缓存节点注册器
            new ModuleRegisToRedisHandler(),
            // 执行计时器
            new TimerWithExecution()
    };

    public static Set<InvokePostProcess<CacheMethod>> invokePostProcess() {
        return new LinkedHashSet<>(Arrays.asList(postProcesses));
    }

    public static void registerAnnotationHandler(CacheEnhancer enhancer) {
        enhancer.setCachePostProcesses(saveCache);
        enhancer.setInvokePostProcesses(postProcesses);
    }

}
